
#include "lib_ffmpeg.h"

#include "../../public/public_msg/public_msg.h"

//****************************************************************

f_para_t f_p;

//****************************************************************
int ffmpeg_get_extradata(uint8_t**ex)
{	
#if 0	
	log_n("avformat =%x\n",f_p.f_avformat);
	log_n("streams  =%x\n",f_p.f_avformat->streams[f_p.f_v_index]);
	log_n("codecpar =%x\n",f_p.f_avformat->streams[f_p.f_v_index]->codecpar);
	log_n("extradata=%x\n",f_p.f_avformat->streams[f_p.f_v_index]->codecpar->extradata);
#endif
	lib_ffmpeg_mutex_lock();

	*ex = f_p.f_avformat->streams[f_p.f_v_index]->codecpar->extradata;
	
	lib_ffmpeg_mutex_unlock();

#if 0	
	log_n("\n");
#endif

	return 0;
}


int  ffmpeg_get_sample_rate(void)
{
	return f_p.f_a_ctx->sample_rate;
}

int  ffmpeg_get_channels(void)
{
	return f_p.f_a_ctx->channels;
}

int  ffmpeg_get_sample_fmt(void)
{
	return f_p.f_a_ctx->sample_fmt;
}

int  ffmpeg_get_frame_size(void)
{
	return f_p.f_a_ctx->frame_size;
}

int ffmpeg_get_video_time_base(int* num, int* den)
{
	if(!num)
		return -1;
	
	if(!den)
		return -1;	

	lib_ffmpeg_mutex_lock();

	*num = f_p.f_avformat->streams[f_p.f_v_index]->time_base.num;
	*den = f_p.f_avformat->streams[f_p.f_v_index]->time_base.den;

	lib_ffmpeg_mutex_unlock();

	return 0;
}

int  ffmpeg_get_audio_time_base(int* num, int* den)
{
    if(!num)
		return -1;
	
    if(!den)
		return -1;	

	lib_ffmpeg_mutex_lock();

	*num = f_p.f_avformat->streams[f_p.f_a_index]->time_base.num;
	*den = f_p.f_avformat->streams[f_p.f_a_index]->time_base.den;

	lib_ffmpeg_mutex_unlock();

	return 0;
}

int ffmpeg_get_fps(int* fps)
{
    if(!fps)
		return -1;
	
	lib_ffmpeg_mutex_lock();
	*fps = f_p.f_fps;
	lib_ffmpeg_mutex_unlock();

	return 0;
}



int ffmpeg_is_stop(void)
{
	int val = 0;
	lib_ffmpeg_mutex_lock();
	val = f_p.f_stop;
	lib_ffmpeg_mutex_unlock();
	
	return val;
}

int ffmpeg_set_video_stop(int val)
{
	lib_ffmpeg_mutex_lock();
	f_p.f_video_stop = val;
	lib_ffmpeg_mutex_unlock();
	
	return 0;
}

int ffmpeg_set_audio_stop(int val)
{
	lib_ffmpeg_mutex_lock();
	f_p.f_audio_stop = val;
	lib_ffmpeg_mutex_unlock();
	
	return 0;
}

int ffmpeg_set_alsa_stop(int val)
{
	lib_ffmpeg_mutex_lock();

	f_p.f_alsa_stop = val;

	lib_ffmpeg_mutex_unlock();
	
	return 0;
}



int ffmpeg_sync_time_init(void)
{
	lib_ffmpeg_mutex_lock();

	f_p.f_start_time =  av_gettime()/1000;

	lib_ffmpeg_mutex_unlock();

	return 0;
}

int64_t ffmpeg_sync_time_get(void)
{
	int64_t  cur_time = 0;
	
	lib_ffmpeg_mutex_lock();

	cur_time = av_gettime()/1000 - f_p.f_start_time;

	lib_ffmpeg_mutex_unlock();

	return cur_time;
}

int64_t ffmpeg_get_v_clock(int pts)
{
	lib_ffmpeg_mutex_lock();

	f_p.f_v_clock = pts * av_q2d(f_p.f_avformat->streams[f_p.f_v_index]->time_base);//s
	f_p.f_v_clock = f_p.f_v_clock*1000;//ms

	lib_ffmpeg_mutex_unlock();

	return f_p.f_v_clock;
}

int64_t ffmpeg_get_a_clock(int pts)
{
	lib_ffmpeg_mutex_lock();

	f_p.f_a_clock = pts * av_q2d(f_p.f_avformat->streams[f_p.f_a_index]->time_base);//s
	f_p.f_a_clock = f_p.f_a_clock*1000;//ms

	lib_ffmpeg_mutex_unlock();

	return f_p.f_a_clock;
}


//****************************************************************
static int ffmpeg_open_stream(const char *file_name)
{
	int i    = 0;
	int ret  = 0;
	int hours, mins, secs, us;
	

	
    AVDictionary * opts = NULL;
	AVDictionary * dict     = NULL;
	//****************************************************************

	av_dict_set(&dict, "rtsp_transport", "tcp", 0);

	//****************************************************************

	f_p.f_avformat = avformat_alloc_context();
	if(!f_p.f_avformat){
		log_e("avformat error\n");
		return -1;
	}
	//****************************************************************

	ret = avformat_open_input(&f_p.f_avformat, file_name, NULL, &dict);
	if(ret != 0){
		log_e("open err=%s\n",file_name);
		avformat_free_context(f_p.f_avformat);
		f_p.f_avformat = NULL;
		return -1;
	}
	//****************************************************************
	ret = avformat_find_stream_info(f_p.f_avformat, NULL);
	if(ret < 0){	
		log_e("no stream\n");

		avformat_close_input(&f_p.f_avformat);
		avformat_free_context(f_p.f_avformat);
		f_p.f_avformat = NULL;
		return -1;
	}	
	//****************************************************************
	
	log_i("************************************************************************************\n");

	av_dump_format(f_p.f_avformat,0, file_name,0);
	
	log_i("************************************************************************************\n");

	log_n("duration=%lld\n",f_p.f_avformat->duration); 
	//****************************************************************

	f_p.f_v_index = -1;
	f_p.f_a_index = -1;

	//****************************************************************
	for(i = 0; i < f_p.f_avformat->nb_streams; i++)
	{
		log_n("codec_type=%d\n",f_p.f_avformat->streams[i]->codecpar->codec_type);
		log_n("time_base =%d\n",f_p.f_avformat->streams[i]->time_base.den);
		log_n("time_base =%d\n",f_p.f_avformat->streams[i]->time_base.num);
		
		if (f_p.f_avformat->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) 
		{
			f_p.f_v_index = i;
		}
		if (f_p.f_avformat->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) 
		{
			f_p.f_a_index = i;
		}
	}		

	//*************************************************************************************
	
	f_p.f_duration = f_p.f_avformat->duration;

	secs  	 = f_p.f_duration / AV_TIME_BASE;
	us	     = f_p.f_duration % AV_TIME_BASE;
	mins     = secs / 60;
	secs    %= 60;
	hours    = mins / 60;
	mins     %= 60;
	
	log_n("duration=%lld\n",f_p.f_duration);
	log_i("duration=%02dhour:%02dminute:%02dsec.%02d\n",hours,mins,secs,(100 * us)/AV_TIME_BASE);
	//*************************************************************************************
	if(f_p.f_v_index == -1)
	{
		log_e("no video stream\n");		
		log_e("nb_streams=%d\n",f_p.f_avformat->nb_streams);
		log_e("a_index =%d\n",f_p.f_a_index);
		log_e("v_index =%d\n",f_p.f_v_index);

		avformat_close_input(&f_p.f_avformat);
		avformat_free_context(f_p.f_avformat);
		f_p.f_avformat = NULL;
		
		return -1;
	}
	//*************************************************************************************
	log_n("v_codec_id =%d\n",f_p.f_avformat->streams[f_p.f_v_index]->codecpar->codec_id);
	log_n("a_codec_id =%d\n",f_p.f_avformat->streams[f_p.f_a_index]->codecpar->codec_id);
	//*************************************************************************************

    f_p.f_v_ctx = avcodec_alloc_context3(NULL);
    if(!f_p.f_v_ctx){
		log_e("avcodec alloc error\n");
		return -1;
	}

    ret = avcodec_parameters_to_context(f_p.f_v_ctx, f_p.f_avformat->streams[f_p.f_v_index]->codecpar);
    if (ret < 0){
		log_e("avcodec parameters error\n");
		return -1;
	}
	//*************************************************************************************

    f_p.f_v_codec = avcodec_find_decoder(f_p.f_v_ctx->codec_id);
	if(f_p.f_v_codec){
		log_n("v name =%s\n",f_p.f_v_codec->name);
	}
	//*************************************************************************************

	f_p.f_v_w  = f_p.f_v_ctx->width;
	f_p.f_v_h  = f_p.f_v_ctx->height;

	log_n("v_w =%d\n",f_p.f_v_w);
	log_n("v_h =%d\n",f_p.f_v_h);
	//*************************************************************************************
	f_p.f_v_stream = f_p.f_avformat->streams[f_p.f_v_index];
	f_p.f_fps      = f_p.f_v_stream->avg_frame_rate.num/f_p.f_v_stream->avg_frame_rate.den;

	log_n("fps =%d\n",f_p.f_fps);
	//*************************************************************************************
	//get audio info 

	if(f_p.f_a_index != -1)
	{
	    f_p.f_a_ctx = avcodec_alloc_context3(NULL);
	    if (!f_p.f_a_ctx){
			log_e("avcodec alloc error\n");
			return -1;
		}

		f_p.f_a_ctx->thread_count = 16;
		
		ret = avcodec_parameters_to_context(f_p.f_a_ctx, f_p.f_avformat->streams[f_p.f_a_index]->codecpar);
		if (ret < 0){
			log_e("avcodec parameters error\n");
			return -1;
		}
	
		f_p.f_a_codec = avcodec_find_decoder(f_p.f_a_ctx->codec_id);
		if(!f_p.f_a_codec){
			log_e("no audio codec.\n");
			return -1;
		}
		
		log_n("a name =%s\n",f_p.f_a_codec->name);

		av_dict_set(&opts, "threads", "auto", 0);
		av_dict_set(&opts, "refcounted_frames", "1", 0);
		av_dict_set(&opts, "timeout", "1000", 0);//3s

		ret = avcodec_open2(f_p.f_a_ctx, f_p.f_a_codec, &opts);
		if(ret < 0){
			log_e("audio codec open error.\n");
			return -1;
		}
		
		//AV_CH_LAYOUT_STEREO
		//AV_SAMPLE_FMT_S16

		log_n("frame_size =%d\n",f_p.f_a_ctx->frame_size);
		log_n("channels   =%d\n",f_p.f_a_ctx->channels);
		log_n("sample_fmt =%d\n",f_p.f_a_ctx->sample_fmt);
		log_n("sample_rate=%d\n",f_p.f_a_ctx->sample_rate);

	}
	//*************************************************************************************
	f_p.f_a_frame = av_frame_alloc();
	if(!f_p.f_a_frame){
		log_e("a frame error.\n");
		return -1;
	}
	//*************************************************************************************

	f_p.f_apacket = av_packet_alloc();
	if(!f_p.f_apacket){
		log_e("a packet error.\n");
		return -1;
	}
	av_init_packet(f_p.f_apacket);

	//*************************************************************************************

	f_p.f_vpacket = av_packet_alloc();
	if(!f_p.f_vpacket){
		log_e("v packet error.\n");
		return -1;
	}
	av_init_packet(f_p.f_vpacket);
	//****************************************************************
	if(f_p.f_a_ctx->sample_fmt == 8)
	{
		f_p.f_s_fmt = AV_SAMPLE_FMT_U8;
	}
	
	if(f_p.f_a_ctx->sample_fmt == 16)
	{
		f_p.f_s_fmt = AV_SAMPLE_FMT_S16;
	}

	if(f_p.f_a_ctx->sample_fmt == 32)
	{
		f_p.f_s_fmt = AV_SAMPLE_FMT_S32;
	}

	f_p.f_s_fmt =  f_p.f_a_ctx->sample_fmt;
	
	f_p.f_in_ch_l = av_get_default_channel_layout(f_p.f_a_ctx->channels);
	//****************************************************************

	f_p.f_acvt_ctx	 = swr_alloc();
	if(!f_p.f_acvt_ctx){
		log_e("swr alloc error\n");
		return -1;
	}

	f_p.f_acvt_ctx = swr_alloc_set_opts(f_p.f_acvt_ctx,AV_CH_LAYOUT_STEREO,AV_SAMPLE_FMT_S16, 
											f_p.f_a_ctx->sample_rate,
											f_p.f_in_ch_l, 
											f_p.f_s_fmt, 
											f_p.f_a_ctx->sample_rate, 0, NULL);
	swr_init(f_p.f_acvt_ctx);

	f_p.f_pcm_buff	= (uint8_t*)av_malloc(MAX_AUDIO_FRAME_SIZE * 2);
	f_p.f_pcm_size  = av_samples_get_buffer_size(NULL, 
												av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO), 
												f_p.f_a_ctx->frame_size,
												AV_SAMPLE_FMT_S16, 1);
	
	log_n("pcm size =%d\n",f_p.f_pcm_size);											
	//****************************************************************

	log_i("open file stream succeed.\n");
	//****************************************************************

	ffmpeg_sync_time_init();
	//****************************************************************

	log_i("v_w =%d\n",f_p.f_v_w);
	log_i("v_h =%d\n",f_p.f_v_h);
	//****************************************************************

	if((f_p.f_v_w > MAX_VIDEO_W)&&(f_p.f_v_h > MAX_VIDEO_H))
	{
		log_i("error:beyond resolution\n");
		return -1;
	}
	else
	{
		return 0;
	}
	//****************************************************************

}
//****************************************************************
static int ffmpeg_close_stream(void)
{
	log_n("close file stream.\n");
	
	if(f_p.f_v_ctx){
		avcodec_free_context(&f_p.f_v_ctx);
		f_p.f_v_ctx = NULL;
	}
	if(f_p.f_v_ctx){
		avcodec_close(f_p.f_v_ctx);
		f_p.f_v_ctx = NULL;
	}
	//***************************************

	if(f_p.f_a_ctx){
		avcodec_free_context(&f_p.f_a_ctx);
		f_p.f_a_ctx = NULL;
	}
	
	if(f_p.f_a_ctx){
		avcodec_close(f_p.f_a_ctx);
		f_p.f_a_ctx = NULL;
	}
	//***************************************

	if(f_p.f_apacket){
		av_packet_free(f_p.f_apacket);
		f_p.f_apacket = NULL;
	}

	
	if(f_p.f_vpacket){
		av_packet_free(f_p.f_vpacket);
		f_p.f_vpacket = NULL;
	}
	//***************************************

	if(f_p.f_avformat)
	{
		if(&f_p.f_avformat)
			avformat_close_input(&f_p.f_avformat);
		avformat_free_context(f_p.f_avformat);
		f_p.f_avformat = NULL;
	}
	//***************************************

	if(f_p.f_a_frame){
		av_frame_free(&f_p.f_a_frame);
		f_p.f_a_frame = NULL;
	}
	//***************************************
	
	if(f_p.f_acvt_ctx){
		swr_free(&f_p.f_acvt_ctx);
		f_p.f_acvt_ctx = NULL;
	}	
	
	if(f_p.f_pcm_buff){
		av_free(f_p.f_pcm_buff);
		f_p.f_pcm_buff = NULL;
	}

	//***************************************

	log_n("close file stream succeed.\n");
	
	return 0;

}
//****************************************************************

static int ffmpeg_read_frame(void)
{
	int ret = 0;
	
	char error_msg[256] ={0};
	  
	ret = av_read_frame(f_p.f_avformat, f_p.f_vpacket);
	if(ret){
		av_packet_unref(f_p.f_vpacket);
		
		memset(error_msg,0,sizeof(error_msg));
		sprintf(error_msg, "msg:%s\n", av_err2str(ret));
		log_e("%s\n",error_msg);
		
		return -1;
	}
	
	if(f_p.f_vpacket->stream_index == f_p.f_v_index)
	{
		queue_packet_write_video(f_p.f_vpacket->data,f_p.f_vpacket->size,f_p.f_vpacket->pts,f_p.f_vpacket->dts);

		//log_d("v_pts=%lld\n",f_p.f_vpacket->pts);
		//log_d("v_dts=%lld\n",f_p.f_vpacket->dts);

		//log_n("v_time = %f\n",f_p.f_vpacket->pts * av_q2d(f_p.f_avformat->streams[f_p.f_v_index]->time_base));
	}
	
	if(f_p.f_vpacket->stream_index == f_p.f_a_index)
	{
		queue_packet_write_audio(f_p.f_vpacket->data,f_p.f_vpacket->size,f_p.f_vpacket->pts);	

		//log_n("a_pts=%d\n",f_p.f_vpacket->pts);
		
		//log_n("a_time = %f\n",f_p.f_vpacket->pts * av_q2d(f_p.f_avformat->streams[f_p.f_a_index]->time_base));
	}
	
	av_packet_unref(f_p.f_vpacket);
	

	return 0;

}

static int ffmpeg_stream_stop(void)
{
	
	ffmpeg_close_stream();

	mpp_para_deinit();

	log_n("\n");

	queue_packet_deinit();
	
	log_n("\n");
	
	alsa_pcm_buff_clear();
	
	log_n("\n");
	alsa_pcm_data_clear();
	
	log_n("\n");
	queue_audio_pcm_deinit();
	
	log_n("\n");

	
	return 0;
}


int ffmpeg_decode_pcm_audio(AVCodecContext *pCodecCtx_audio, AVFrame *frame, AVPacket *avpkt)
{
	int ret  = -1;	
	char error_msg[256] ={0};

	if(!pCodecCtx_audio)
		return -1;
	
	if(!frame)
		return -1;

	if(!avpkt)
		return -1;

	if(!avpkt->data)
		return -1;

	if(!avpkt->size)
		return -1;	


	ret = avcodec_send_packet(pCodecCtx_audio, avpkt);
	av_packet_unref(avpkt);

	if (ret < 0){
		memset(error_msg,0,sizeof(error_msg));
		sprintf(error_msg, "error:%s\n", av_err2str(ret));
		log_e("%s\n",error_msg);
		return -1;
	}
	
	ret = 0;
	while(ret == 0)
	{
		ret = avcodec_receive_frame(pCodecCtx_audio, frame);
		if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
		{
			memset(error_msg,0,sizeof(error_msg));
			sprintf(error_msg, "error:%s\n", av_err2str(ret));
			log_e("%s\n",error_msg);
		
			break;
		}
		else if (ret < 0)
		{
			memset(error_msg,0,sizeof(error_msg));
			sprintf(error_msg, "error:%s\n", av_err2str(ret));
			log_e("%s\n",error_msg);

			break;
		}
		else
		{
			if(f_p.f_acvt_ctx&&f_p.f_pcm_buff){
				ret = swr_convert(f_p.f_acvt_ctx, &f_p.f_pcm_buff, MAX_AUDIO_FRAME_SIZE, &frame->data, frame->nb_samples);
			}
		
			if(ret < 0){
				memset(error_msg,0,sizeof(error_msg));
				sprintf(error_msg, "error:%s\n", av_err2str(ret));
				log_e("%s\n",error_msg);
			}else{
				queue_audio_pcm_write(f_p.f_pcm_buff,f_p.f_pcm_size,frame->pts);
			}
		}
	}

	return 0;
}

void* ffmpeg_decode_thread(void *arg)
{
	int       ret = -1;
	int64_t   audio_pts   = 0;
	int64_t   packet_size = 0;
	uint8_t*  packet_data = NULL;

	while(1)
	{
		usleep(1000);
		
		if(ffmpeg_is_stop())
		{		
			ffmpeg_set_audio_stop(1);
			continue;
		}
		
		ffmpeg_set_audio_stop(0);

		if(queue_packet_video_is_empty()){
			continue;
		}

		ret = queue_packet_read_audio(&packet_data,&packet_size,&audio_pts);
		if(ret != 0){
			continue;
		}

		f_p.f_apacket->data = packet_data;
		f_p.f_apacket->size = packet_size;
		f_p.f_apacket->pts  = audio_pts;
	
		ffmpeg_decode_pcm_audio(f_p.f_a_ctx,f_p.f_a_frame,f_p.f_apacket);

		//log_n("a_clock = %f\n",f_p.f_a_clock);
	
	}

}


//****************************************************************
void* ffmpeg_packet_thread(void *arg)
{
	int ret = -1;

	while(1)
	{
		switch(f_p.f_state){
		case DECODE_STREAM:
		{
			ret = ffmpeg_open_stream(f_p.f_name);
			if(ret != 0)
			{
				f_p.f_state = DECODE_IDLE;
			}
			else
			{
				f_p.f_state = DECODE_READ_PACKET;
			}

			f_p.f_ready = 0;

			break;
		}
		case DECODE_READ_PACKET:
		{
			ret = ffmpeg_read_frame();
			if(ret != 0)
			{
				queue_packet_query_info();

				f_p.f_state = DECODE_IDLE;
			}
			else
			{			
	 			f_p.f_stop = 0;
			}
			break;
		}
		case DECODE_PAUSE:
		{

			break;
		}
		case DECODE_STOP:
		{
			log_d("video stop,play next...\n");

			while(1)
			{
				if(f_p.f_video_stop&f_p.f_audio_stop&f_p.f_alsa_stop)
				{
					break;
				}
				usleep(1000);
			}

			ffmpeg_stream_stop();

			f_p.f_ready = 0;
			f_p.f_state = DECODE_STREAM;

			break;
		}
		case DECODE_IDLE:
		{
			if(f_p.f_ready)
			{
				f_p.f_ready = 0;
				f_p.f_state = DECODE_STREAM;
			}

			if(f_p.f_stop)
			{
				
			}
			else
			{				
				if(queue_packet_video_is_empty())
				{
					f_p.f_stop = 1;

					log_n("video end,play next...\n");
					
					ffmpeg_stream_stop();
					
					message_packet_send(MSG_VIDEO_END,0,0);
				}
			}
			break;
		}
		default:break;
		}
		usleep(1000);
	}
}
//****************************************************************

int ffmpeg_decode_file(const char * filename)
{
	if(!filename){
		return -1;
	}
	
	lib_ffmpeg_mutex_lock();

	f_p.f_state = DECODE_STREAM;

	if(!f_p.f_ready)
	{
		f_p.f_ready = 1;
		
		memset(f_p.f_name,0,sizeof(f_p.f_name));
		memcpy(f_p.f_name,filename,strlen(filename));
	}
	
	lib_ffmpeg_mutex_unlock();
	
	return 0;
}

int ffmpeg_stop_file(void)
{
	lib_ffmpeg_mutex_lock();

	f_p.f_state = DECODE_STOP;
	f_p.f_stop  = 1;
	
	lib_ffmpeg_mutex_unlock();

	return 0;
}

//****************************************************************

int lib_ffmpeg_mutex_lock(void)
{
	if(f_p.f_mutex)
		SDL_LockMutex(f_p.f_mutex);

	return 0;
}

int lib_ffmpeg_mutex_unlock(void)
{
	if(f_p.f_mutex)
		SDL_UnlockMutex(f_p.f_mutex);

	return 0;
}


int lib_ffmpeg_init(void)
{
	int ret = -1;
	
	log_n("lib ffmpeg init.\n");

	//************************************************
	memset(&f_p,0,sizeof(f_para_t));

	f_p.f_mutex = SDL_CreateMutex();

	f_p.f_state = DECODE_IDLE;
	f_p.f_stop  = 1;
	//************************************************

	queue_packet_init();

	avdevice_register_all();
	
	avformat_network_init();
	//************************************************

	ret=pthread_create(&f_p.f_read_thread,NULL,ffmpeg_packet_thread,NULL);
	if(ret != 0){
		log_e("error=%s\n",strerror(ret));
	}
	
	ret=pthread_create(&f_p.f_a_dec_thread,NULL,ffmpeg_decode_thread,NULL);
	if(ret != 0){
		log_e("error=%s\n",strerror(ret));
	}
	//************************************************
	

	return 0;
}

//****************************************************************

int lib_ffmpeg_deinit(void)
{
	log_i("lib ffmpeg exit.\n");

	queue_packet_deinit();

	if(f_p.f_mutex){
		SDL_DestroyMutex(f_p.f_mutex);
		f_p.f_mutex = NULL;
	}


	return 0;
}


//****************************************************************





